package com.drpclub.gamebackend.service;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.LineCaptcha;
import cn.hutool.captcha.generator.MathGenerator;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.drpclub.gamebackend.bean.EnumConstants;
import com.drpclub.gamebackend.bean.User;
import com.drpclub.gamebackend.common.constant.Const;
import com.drpclub.gamebackend.common.utils.Cache;
import com.drpclub.gamebackend.common.utils.DrpUtils;
import com.drpclub.gamebackend.common.utils.FileUtils;
import com.drpclub.gamebackend.common.utils.JwtUtils;
import com.drpclub.gamebackend.dao.RoleDao;
import com.drpclub.gamebackend.dao.UserDao;
import com.drpclub.gamebackend.exception.IgnoreException;
import com.drpclub.gamebackend.repository.UserRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

import static com.drpclub.gamebackend.common.constant.Const.*;
import static com.drpclub.gamebackend.common.utils.DrpUtils.isNotNull;
import static com.drpclub.gamebackend.common.utils.DrpUtils.verifyImageType;
import static com.drpclub.gamebackend.common.utils.FileUtils.*;
import static com.drpclub.gamebackend.common.utils.SecurityUtils.securityHmac;


/**
 * @author dongruipeng
 * @Descrpition
 * @date 2019year 12month04day  14:11:47
 */
@Slf4j
@Service
public class UserService {
    private final static String DEFAULT_HEAD_IMAGE = getHeaderImgPath (DEFAULT_IMAGE, "default");
    @Resource
    private UserRepository userRepository;
    @Resource
    private FileUtils fileUtils;

    @Transactional(value = Const.TRANSACTION_MANAGER, rollbackFor = Exception.class)
    public User register(User user) {
        if (userRepository.countByUsername (user.getUsername ()) > 0) {
            throw new IgnoreException (USERNAME_ALREADY_EXIST);
        }
        user.setNickname (DrpUtils.createRandomName ());
        UserDao userDao = new UserDao ();
        BeanUtil.copyProperties (user, userDao, CopyOptions.create ().setIgnoreNullValue (true));
        //set roles
        final ArrayList<RoleDao> roles = new ArrayList<> ();
        roles.add (new RoleDao (EnumConstants.RoleType.normal));
        userDao.setRoles (roles);
        //set default image
        userDao.setHeadImage (DEFAULT_HEAD_IMAGE);
        //secure password
        userDao.setPassword (securityHmac (user.getPassword ()));

        userDao = userRepository.save (userDao);
        //generate token
        final String token = JwtUtils.generateJwtToken (userDao.getUsername ());
        user.setHeadImage (userDao.getHeadImage ());
        return user.resetPassword ().setToken (token);
    }

    public User login(String username, String password, String validCode) {
        //TODO Cache need optimized
        if (!Cache.verifyCode (username, validCode)) {
            throw new IgnoreException ("验证码错误");
        }
        final UserDao user_ =
                userRepository.findAllByUsernameAndPassword (
                        username, securityHmac (password));

        isNotNull (user_, INVALID_USER);

        final User user = new User ();
        BeanUtil.copyProperties (user_, user);
        final String token = JwtUtils.generateJwtToken (user.getUsername ());
        return user.setToken (token).resetPassword ();
    }

    /**
     * 生成验证码
     *
     * @return 返回base64编码
     */
    public String generateValidCode(String username) {
        final LineCaptcha captcha = CaptchaUtil.createLineCaptcha (100, 50, 4, 2);
        captcha.setGenerator (new MathGenerator ());
        Cache.getCodeMap ().put (username, captcha.getCode ());
        return captcha.getImageBase64 ();
    }

    /**
     * 修改密码
     *
     * @return 修改成功信息
     */
    @Transactional(value = Const.TRANSACTION_MANAGER, rollbackFor = {Exception.class})
    public String changePassword(String token, String oldPassword, String newPassword) {
        final String username = JwtUtils.getUsernameFromToken (token);
        isNotNull (username, Const.INVALID_USERNAME);

        final String secPass = securityHmac (oldPassword);
        final UserDao user = userRepository.findAllByUsernameAndPassword (username, secPass);
        if (user == null) throw new IgnoreException (WRONG_PASSWORD);
        user.setPassword (securityHmac (newPassword));
        userRepository.save (user);
        return CHANGE_PASSWORD_SUCCESSFUL;
    }

    /**
     * 修改用户头像
     * <p>
     * TODO 检测用户现有头像数量，超过一定数量删除旧的头像
     */
    @Transactional(value = Const.TRANSACTION_MANAGER, rollbackFor = {Exception.class})
    public String changeHeadImage(MultipartFile headImage, String token) throws IOException {
        //check file security
        final String type = verifyImageType (headImage.getInputStream ());
        final String username = JwtUtils.getUsernameFromToken (token);
        //upload file
        final String filePath = fileUtils.getFileSaveRealPath (HEAD_IMAGE_PATH, true) + "/" + username;
        //verify already have user headImage
        final File file = new File (filePath);
        if (file.exists ()) {
            final File[] files = isNotNull (file.listFiles (), username + " headerImage is null");
            if (files.length > 2) {
                final List<File> fileList = Arrays.stream (files)
                        .sorted (Comparator.comparingLong (f -> Long.parseLong (
                                f.getName ().substring (0, f.getName ().indexOf ("."))
                        ))).collect (Collectors.toList ());
                final boolean isDelete = fileList.get (0).delete ();
                if (!isDelete) {
                    throw new IgnoreException ("删除现有头像失败");
                }
            }
        }

        String filename = System.currentTimeMillis () + "." + type;
        transMultipartFileTo (headImage, filename, filePath);
        //update databases
        final UserDao user = userRepository.findAllByUsername (username);
        isNotNull (user, INVALID_USER);
        user.setHeadImage (getHeaderImgPath (filename, username));
        UserDao user_ = userRepository.save (user);
        return fullFilePath (user_.getHeadImage ());
    }

    @Transactional(value = TRANSACTION_MANAGER, rollbackFor = {Exception.class})
    public int changeNickname(String nickname, String token) {
        String username = JwtUtils.getUsernameFromToken (token);
        return userRepository.updateNicknameByUsername (username, nickname);
    }

    public User getUserInfo(String token) {
        String username = JwtUtils.getUsernameFromToken (isNotNull(token, "token"));
        UserDao userDao = userRepository.findAllByUsername (username);
        User user = new User ();
        BeanUtil.copyProperties (userDao, user);
        return user.resetPassword ();
    }
}
